<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="类型注解 typing 模块">
<meta name="twitter:description" content="<p>VScode中使用类型检查；类型注解；typing模块：各种类型的定义和使用；参考项目</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/python-type-hints.html"/>
<meta property="og:title" content="来玩魔王的咚 | 类型注解 typing 模块" />
<meta property="og:description" content="<p>VScode中使用类型检查；类型注解；typing模块：各种类型的定义和使用；参考项目</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>VScode中使用类型检查；类型注解；typing模块：各种类型的定义和使用；参考项目</p>" />

    <meta name="tags" content="python模块" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li class="active"><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2020-11-28 20:00:00+08:00">
                <script>document.write(moment('2020-11-28 20:00:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2020-11-28 20:00:00+08:00">
                <script>document.write(moment('2020-11-29 16:40:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            类型注解 typing 模块
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#vscode-zhong-kai-qi">VScode中开启</a></li>
<li><a href="#lei-xing-zhu-jie">类型注解</a><ul>
<li><a href="#fu-he-lei-xing-de-sheng-ming">复合类型的声明</a></li>
</ul>
</li>
<li><a href="#typing-mo-kuai">typing 模块</a></li>
<li><a href="#can-kao-xiang-mu">参考项目</a></li>
</ul>
</div>
<h3 id="vscode-zhong-kai-qi"><a class="toclink" href="#vscode-zhong-kai-qi">VScode中开启</a></h3>
<p>在VScode中，默认没有类型检查的功能，推荐安装Pylance扩展。这个是语言服务器（Language Server），功能不是类型检查，但是包括了类型检查的功能。<br>
设置中搜<code>Language Server</code>，找到Python中的对应选项，默认使用的是Jedi，改成使用Pylance。<br>
然后还要开启Pylance的类型检查（Type Checking Mode），默认是off关闭的，选择basci或strict开启这个功能。  </p>
<h3 id="lei-xing-zhu-jie"><a class="toclink" href="#lei-xing-zhu-jie">类型注解</a></h3>
<p>在Python3.5中，PEP 484引入了类型注解（type hints），在Python3.6中，PEP 526又进一步引入了变量注解（Variable Annotations）。  </p>
<p>具体的语法是可以归纳为两点：</p>
<ol>
<li>在声明变量时，变量的后面可以加一个冒号，后面再写上变量的类型，如  int、list 等等。</li>
<li>在声明方法返回值的时候，可以在方法的后面加一个箭头，后面加上返回值的类型，如 int、list 等等。</li>
</ol>
<p>代码风格PEP 8中对于格式规定的详细描述：</p>
<ol>
<li>在声明变量类型时，变量后方紧跟一个冒号，冒号后面跟一个空格，再跟上变量的类型。</li>
<li>在声明方法返回值的时候，箭头左边是方法定义，箭头右边是返回值的类型，箭头左右两边都要留有空格。</li>
</ol>
<p>示例如下：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> 
</code></pre></div>

<h4 id="fu-he-lei-xing-de-sheng-ming"><a class="toclink" href="#fu-he-lei-xing-de-sheng-ming">复合类型的声明</a></h4>
<p>先看下如下的错误的类型声明：</p>
<div class="highlight"><pre><span></span><code><span class="n">names</span><span class="p">:</span> <span class="nb">list</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;Germey&#39;</span><span class="p">,</span> <span class="s1">&#39;Guido&#39;</span><span class="p">]</span>
<span class="n">version</span><span class="p">:</span> <span class="nb">tuple</span> <span class="o">=</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="n">operations</span><span class="p">:</span> <span class="nb">dict</span> <span class="o">=</span> <span class="p">{</span><span class="s1">&#39;show&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;sort&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">}</span>
</code></pre></div>

<p>这样的声明意义不大，内部的类型没有声明。<em>而且这么做，编译器会提出有问题。</em>  </p>
<p>下面的正确的做法：</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">,</span> <span class="n">Dict</span>
<span class="n">names</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;Germey&#39;</span><span class="p">,</span> <span class="s1">&#39;Guido&#39;</span><span class="p">]</span>
<span class="n">version</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="n">operations</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s1">&#39;show&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;sort&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">}</span>
</code></pre></div>

<p>这里导入了typing模块。  </p>
<h3 id="typing-mo-kuai"><a class="toclink" href="#typing-mo-kuai">typing 模块</a></h3>
<p>详细了解下typing模块里的注解类型。  </p>
<p><strong>List 列表</strong>，是list的泛型，基本等同于list，其后紧跟一个方括号，里面代表了构成这个列表的元素类型。<br>
元素可以是几种类型都算合法的情况：</p>
<div class="highlight"><pre><span></span><code><span class="n">var</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span> <span class="ow">or</span> <span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mf">3.5</span><span class="p">]</span>
</code></pre></div>

<p>还可以是嵌套的声明：</p>
<div class="highlight"><pre><span></span><code><span class="n">var</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]]</span> <span class="o">=</span> <span class="p">[[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]]</span>
</code></pre></div>

<p><strong>Tuple 元组</strong>，是tuple的泛型，其后紧跟一个方括号，方括号中按照顺序声明了构成本元组的元素类型。<br>
声明一个元组，元素分别代表姓名str、年龄int、身高float，声明如下：</p>
<div class="highlight"><pre><span></span><code><span class="n">person</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;Mike&#39;</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mf">1.75</span><span class="p">)</span>
</code></pre></div>

<p>可用省略号字面量指定同质变长元组：</p>
<div class="highlight"><pre><span></span><code><span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span>
<span class="c1"># 下面2个类型是等价的</span>
<span class="n">Tuple</span>
<span class="n">Tuple</span><span class="p">[</span><span class="n">Any</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span>
</code></pre></div>

<p><strong>NamedTuple 具名元组</strong>，是collections.namedtuple的泛型。<br>
这里不是声明了，而是定义类型的时候，就直接使用typing模块来定义：</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">namedtuple</span>
<span class="n">Person1</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span><span class="s2">&quot;Person&quot;</span><span class="p">,</span> <span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="s2">&quot;age&quot;</span><span class="p">,</span> <span class="s2">&quot;high&quot;</span><span class="p">])</span>

<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">NamedTuple</span>
<span class="n">Person2</span> <span class="o">=</span> <span class="n">NamedTuple</span><span class="p">(</span><span class="s2">&quot;Person&quot;</span><span class="p">,</span> <span class="p">[(</span><span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="p">,</span> <span class="p">(</span><span class="s2">&quot;age&quot;</span><span class="p">,</span> <span class="nb">int</span><span class="p">),</span> <span class="p">(</span><span class="s2">&quot;high&quot;</span><span class="p">,</span> <span class="nb">float</span><span class="p">)])</span>

<span class="c1"># 这个是具名元组更加清晰定义语法，建议用这个</span>
<span class="k">class</span> <span class="nc">Person</span><span class="p">(</span><span class="n">NamedTuple</span><span class="p">):</span>
    <span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">age</span><span class="p">:</span> <span class="nb">int</span>
    <span class="n">high</span><span class="p">:</span> <span class="nb">float</span>
</code></pre></div>

<p><strong>Dict 字典</strong>，是dict的泛型。<br>
<strong>Mapping 映射</strong>，是collections.abc.Mapping的泛型。<br>
这两个一起讲，根据官方文档，Dict推荐用于注解返回类型，Mapping推荐用于注解参数。它们的使用方法都是一样的，其后跟一个中括号，中括号内分别声明key和Value。声明使用如下：  </p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">size</span><span class="p">(</span><span class="n">rect</span><span class="p">:</span> <span class="n">Mapping</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">]:</span>
    <span class="k">return</span> <span class="p">{</span><span class="s1">&#39;width&#39;</span><span class="p">:</span> <span class="n">rect</span><span class="p">[</span><span class="s1">&#39;width&#39;</span><span class="p">]</span> <span class="o">+</span> <span class="mi">100</span><span class="p">,</span> <span class="s1">&#39;height&#39;</span><span class="p">:</span> <span class="n">rect</span><span class="p">[</span><span class="s1">&#39;width&#39;</span><span class="p">]</span> <span class="o">+</span> <span class="mi">100</span><span class="p">}</span>
</code></pre></div>

<p>这里参数的类型用的是Mapping，返回值的注解是Dict。<br>
我的理解：在collections.abc模块中定义的是抽象基类。<em>标准库中大多数的抽象基类都在这个模块这定义，少量的比如numbers和io包中也有一些，但可能不常用。</em>参数是给调用者使用的，调用函数的时候可以传入字典也可以传入实现了字典协议的其他类型，这样做并不影响函数的实现。所以不用关注具体的类型，只要它的行为像就可以了。而返回值是函数可以控制的，所以可以明确的知道具体的类型。<br>
我的理解2：如果返回值是对传入的Mapping类型操作后再把那个变量返回，那么返回值也应该继续使用抽象类。但是这种情况本身似乎也不合理，因为不用将变量返回，操作是直接作用在传入的变量上的，不需要获取返回值。而如果是将传入的变量复制一份，再操作返回，那么这种时候似乎又需要明确知道生产的变量的具体类型。  </p>
<p><strong>MutableMapping 可变映射</strong>，是Mapping对象的子类。Mapping是不可变类型，MutableMapping是这个类型可变的子类。类型注解的时候可能有混用的情况（如果不是作者不清楚，就是看的人不清楚了）。  </p>
<p><strong>Set 集合</strong>，是set的泛型。<br>
<strong>AbstractSet</strong>，是collections.abc.Set的泛型。<br>
又是一对具体类型和抽象类，声明如下：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">describe</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="n">AbstractSet</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">Set</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
    <span class="k">return</span> <span class="nb">set</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
</code></pre></div>

<p><strong>Sequence 序列</strong>，是collections.abc.Sequence的泛型。可以当做列表的抽象类，并且同时也可以作为元组的抽象类。很多时候，并不需要严格区分是列表还是元组。用法同列表类型：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">square</span><span class="p">(</span><span class="n">elements</span><span class="p">:</span> <span class="n">Sequence</span><span class="p">[</span><span class="nb">float</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">float</span><span class="p">]:</span>
    <span class="k">return</span> <span class="p">[</span><span class="n">x</span> <span class="o">**</span> <span class="mi">2</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">elements</span><span class="p">]</span>
</code></pre></div>

<p><strong>NoReturn</strong>，是没有返回（不是没有返回值，返回空算是有返回），函数要么不终止（一直循环），要么抛出异常。<br>
这里要和返回None区分开。<code>return</code>、<code>return None</code>或函数运行结束自动返回，都是None，应该用None类型。<br>
正确的用法如下：</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">NoReturn</span>

<span class="k">def</span> <span class="nf">raise_err</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">NoReturn</span><span class="p">:</span>
    <span class="k">raise</span> <span class="ne">AssertionError</span><span class="p">(</span><span class="s2">&quot;oops an error&quot;</span><span class="p">)</span>
</code></pre></div>

<p>这种才是NoReturn，没有返回。  </p>
<p><strong>Any</strong>，是一种特殊的类型，它可以代表所有类型。既要写注解，又不用做任何的类型限制，就用这个了：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">Any</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Any</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="mi">1</span>
</code></pre></div>

<p>原理类似于object，所有的类型都是object的子类。但是不能直接用object做标注，可以用Any。  </p>
<p><strong>NewType</strong>，声明一个具体的类型，之后再用这个类型声明具体的变量。这样做是为了看起来更直观：</p>
<div class="highlight"><pre><span></span><code><span class="n">person1</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;Mike&#39;</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mf">1.75</span><span class="p">)</span>
<span class="c1"># NewType 的用法</span>
<span class="n">Person</span> <span class="o">=</span> <span class="n">NewType</span><span class="p">(</span><span class="s1">&#39;Person&#39;</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">])</span>
<span class="n">person2</span> <span class="o">=</span> <span class="n">Person</span><span class="p">((</span><span class="s1">&#39;Mike&#39;</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mf">1.75</span><span class="p">))</span>
</code></pre></div>

<p>这里person1和person2都是一样的元组类型。  </p>
<p><strong>Callable 可调用类型</strong>，通常用来注解一个方法。声明的时候需要将参数和返回值的类型都标注出来：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">date</span><span class="p">(</span><span class="n">year</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">month</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">day</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="k">return</span> <span class="sa">f</span><span class="s1">&#39;</span><span class="si">{</span><span class="n">year</span><span class="si">}</span><span class="s1">-</span><span class="si">{</span><span class="n">month</span><span class="si">}</span><span class="s1">-</span><span class="si">{</span><span class="n">day</span><span class="si">}</span><span class="s1">&#39;</span>

<span class="k">def</span> <span class="nf">get_date_fn</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">],</span> <span class="nb">str</span><span class="p">]:</span>
    <span class="k">return</span> <span class="n">date</span>
</code></pre></div>

<p>这是预期特定签名回调函数的形式。  </p>
<p>还有无需指定调用签名的形式，用省略号字面量替换参数列表的部分：</p>
<div class="highlight"><pre><span></span><code><span class="n">Callable</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="n">ReturnType</span><span class="p">]</span>
</code></pre></div>

<p>就是可接受任意数量参数，并返回 ReturnType 的可调用对象的类型提示。  </p>
<p><strong>Union 联合类型</strong>，可以是几个类型中的任意一种。<br>
在一些方法参数声明的时候比较有用，比如一个方法，要么传一个字符串表示的方法名，要么直接把方法传过来：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="n">fn</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Callable</span><span class="p">]):</span>
    <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
        <span class="c1"># str2fn and process</span>
        <span class="k">pass</span>
    <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="n">Callable</span><span class="p">):</span>
        <span class="n">fn</span><span class="p">()</span>
</code></pre></div>

<p><strong>TypeVar</strong>，用来自定义兼容特定类型的变量。有些时候某几种类型都合理，那么就用这个：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># 先自定义好类型，赋值给变量，之后直接使用变量名作为类型的名称</span>
<span class="n">Height</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">&#39;Height&#39;</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_height</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">Height</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">height</span>
</code></pre></div>

<p>使用TypeVar声明了一个Height类型，可以是int、float或None。  </p>
<p><strong>TypeVar 和 Union的差别</strong><br>
平时用的时候，根据使用场景的习惯来选择应该就可以了。下面比较下差异和容易出错的地方。  </p>
<p>在单个范围内，TypeVar的类型必须一样：</p>
<div class="highlight"><pre><span></span><code><span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">&quot;T&quot;</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">T</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">T</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span>

<span class="n">add</span><span class="p">(</span><span class="mi">1</span> <span class="p">,</span><span class="mi">2</span><span class="p">)</span>     <span class="c1"># 正确</span>
<span class="n">add</span><span class="p">(</span><span class="s1">&#39;1&#39;</span><span class="p">,</span> <span class="s1">&#39;2&#39;</span><span class="p">)</span> <span class="c1"># 正确</span>
<span class="n">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;2&#39;</span><span class="p">)</span>   <span class="c1"># 期望是int，第二个参数是str</span>
<span class="n">add</span><span class="p">(</span><span class="s1">&#39;1&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>   <span class="c1"># 期望是str，第二个参数是int</span>
</code></pre></div>

<p>所以，TypeVar的类型还是有约束的，而且需要命名后使用。下面是错误的用法：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">&quot;T&quot;</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">),</span> <span class="n">y</span><span class="p">:</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">&quot;T&quot;</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">))</span> <span class="o">-&gt;</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">&quot;T&quot;</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span>

<span class="n">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;2&#39;</span><span class="p">)</span>   <span class="c1"># 无警告</span>
<span class="n">add</span><span class="p">(</span><span class="s1">&#39;1&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>   <span class="c1"># 无警告</span>
</code></pre></div>

<p>命名后使用不只是减少重复代码，和可读性的问题。单个范围内约束的功能也没了。  </p>
<p>而Union看似更宽松，但使用不当也是会提示警告的：</p>
<div class="highlight"><pre><span></span><code><span class="n">U</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">U</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">U</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">U</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span>  <span class="c1"># 这里会报警，检查到int和str无法相加</span>
</code></pre></div>

<p><strong>Optional</strong>，可以是参数声明的类型也可以是None，即<code>Optional[X]</code>等价于<code>Union[X, None]</code>。<br>
这个场景还是比较多了，有值就返回值，没有值就返回None。下面是示例：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">judge</span><span class="p">(</span><span class="n">result</span><span class="p">:</span> <span class="nb">bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
    <span class="k">if</span> <span class="n">result</span><span class="p">:</span> <span class="k">return</span> <span class="s1">&#39;Error Occurred&#39;</span>
</code></pre></div>

<p><strong>Generator 生成器</strong>，声明比较特殊，其后的中括号紧跟着三个参数，分别代表YieldType、SendType、ReturnType。示例如下：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">echo_round</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">Generator</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
    <span class="n">sent</span> <span class="o">=</span> <span class="k">yield</span> <span class="mi">0</span>
    <span class="k">while</span> <span class="n">sent</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">sent</span> <span class="o">=</span> <span class="k">yield</span> <span class="nb">round</span><span class="p">(</span><span class="n">sent</span><span class="p">)</span>
    <span class="k">return</span> <span class="s1">&#39;Done&#39;</span>
</code></pre></div>

<p>yield关键字后面紧跟的变量的类型是YieldType；<br>
yield返回的结果的类型就是SendType；<br>
最后生成器return的类型是ReturnType。  </p>
<h3 id="can-kao-xiang-mu"><a class="toclink" href="#can-kao-xiang-mu">参考项目</a></h3>
<p>参考项目：<a href="https://github.com/psf/requests-html">https://github.com/psf/requests-html</a> 。<br>
具体看这个文件：<a href="https://github.com/psf/requests-html/blob/master/requests_html.py">https://github.com/psf/requests-html/blob/master/requests_html.py</a> 。<br>
库里的源代码也就这个文件。  </p>
<p>在开头部分先声明了一系列的类型：</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Set</span><span class="p">,</span> <span class="n">Union</span><span class="p">,</span> <span class="n">List</span><span class="p">,</span> <span class="n">MutableMapping</span><span class="p">,</span> <span class="n">Optional</span>

<span class="c1"># Typing.</span>
<span class="n">_Find</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="s1">&#39;Element&#39;</span><span class="p">],</span> <span class="s1">&#39;Element&#39;</span><span class="p">]</span>
<span class="n">_XPath</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">List</span><span class="p">[</span><span class="s1">&#39;Element&#39;</span><span class="p">],</span> <span class="nb">str</span><span class="p">,</span> <span class="s1">&#39;Element&#39;</span><span class="p">]</span>
<span class="n">_Result</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="s1">&#39;Result&#39;</span><span class="p">],</span> <span class="s1">&#39;Result&#39;</span><span class="p">]</span>
<span class="n">_HTML</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">]</span>
<span class="n">_BaseHTML</span> <span class="o">=</span> <span class="nb">str</span>
<span class="n">_UserAgent</span> <span class="o">=</span> <span class="nb">str</span>
<span class="n">_DefaultEncoding</span> <span class="o">=</span> <span class="nb">str</span>
<span class="n">_URL</span> <span class="o">=</span> <span class="nb">str</span>
<span class="n">_RawHTML</span> <span class="o">=</span> <span class="nb">bytes</span>
<span class="n">_Encoding</span> <span class="o">=</span> <span class="nb">str</span>
<span class="n">_LXML</span> <span class="o">=</span> <span class="n">HtmlElement</span>
<span class="n">_Text</span> <span class="o">=</span> <span class="nb">str</span>
<span class="n">_Search</span> <span class="o">=</span> <span class="n">Result</span>
<span class="n">_Containing</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span>
<span class="n">_Links</span> <span class="o">=</span> <span class="n">Set</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
<span class="n">_Attrs</span> <span class="o">=</span> <span class="n">MutableMapping</span>
<span class="n">_Next</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="s1">&#39;HTML&#39;</span><span class="p">,</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span>
<span class="n">_NextSymbol</span> <span class="o">=</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
</code></pre></div>

<p>然后是使用，看下类的声明：</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Element</span><span class="p">(</span><span class="n">BaseParser</span><span class="p">):</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">element</span><span class="p">,</span> <span class="n">url</span><span class="p">:</span> <span class="n">_URL</span><span class="p">,</span> <span class="n">default_encoding</span><span class="p">:</span> <span class="n">_DefaultEncoding</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">Element</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">element</span><span class="o">=</span><span class="n">element</span><span class="p">,</span> <span class="n">url</span><span class="o">=</span><span class="n">url</span><span class="p">,</span> <span class="n">default_encoding</span><span class="o">=</span><span class="n">default_encoding</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">element</span> <span class="o">=</span> <span class="n">element</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">tag</span> <span class="o">=</span> <span class="n">element</span><span class="o">.</span><span class="n">tag</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">lineno</span> <span class="o">=</span> <span class="n">element</span><span class="o">.</span><span class="n">sourceline</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span> <span class="o">=</span> <span class="kc">None</span>

    <span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
        <span class="n">attrs</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;</span><span class="si">{}</span><span class="s1">=</span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">attr</span><span class="p">,</span> <span class="nb">repr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">attrs</span><span class="p">[</span><span class="n">attr</span><span class="p">]))</span> <span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">attrs</span><span class="p">]</span>
        <span class="k">return</span> <span class="s2">&quot;&lt;Element </span><span class="si">{}</span><span class="s2"> </span><span class="si">{}</span><span class="s2">&gt;&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">repr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">element</span><span class="o">.</span><span class="n">tag</span><span class="p">),</span> <span class="s1">&#39; &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">attrs</span><span class="p">))</span>

    <span class="nd">@property</span>
    <span class="k">def</span> <span class="nf">attrs</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">_Attrs</span><span class="p">:</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span> <span class="o">=</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="n">v</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">element</span><span class="o">.</span><span class="n">items</span><span class="p">()}</span>

            <span class="c1"># Split class and rel up, as there are ussually many of them:</span>
            <span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="p">[</span><span class="s1">&#39;class&#39;</span><span class="p">,</span> <span class="s1">&#39;rel&#39;</span><span class="p">]:</span>
                <span class="k">if</span> <span class="n">attr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span><span class="p">:</span>
                    <span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span><span class="p">[</span><span class="n">attr</span><span class="p">]</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span><span class="p">[</span><span class="n">attr</span><span class="p">]</span><span class="o">.</span><span class="n">split</span><span class="p">())</span>

        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span>
</code></pre></div>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>